﻿#region Copyright information
// 
// Copyright © 2005-2013 Yongkee Cho. All rights reserved.
// 
// This code is a part of the Biological Object Library and governed under the terms of the
// GNU Lesser General  Public License (LGPL) version 2.1 which accompanies this distribution.
// For more information on the LGPL, please visit http://bol.codeplex.com/license.
// 
// - Filename: Chromosome.cs
// - Author: Yongkee Cho
// - Email: yongkeecho@gmail.com
// - Date Created: 2013-01-25 3:25 PM
// - Last Modified: 2013-01-25 3:59 PM
// 
#endregion
using System;
using System.Text.RegularExpressions;
using System.Runtime.Serialization;

namespace BOL.Models.Genome
{
    /// <summary>
    /// Represents the chromosome class
    /// </summary>
    [DataContract]
    public class Chromosome : ICloneable, INamed, IComparable, IComparable<Chromosome>, IEquatable<Chromosome>
    {
        #region Public properties

        /// <summary>Gets or sets taxonomy id.</summary>
        [DataMember]
        public int TaxonomyId { get; private set; }

        /// <summary>Gets or sets chromosome type.</summary>
        [DataMember]
        public ChromosomeType ChromosomeType { get; private set; }

        #endregion

        #region Constructors

        public Chromosome(int taxonomyId, string name)
        {
            if (string.IsNullOrEmpty(name))
                throw new ArgumentNullException("name", "Name cannot be null or empty.");

            TaxonomyId = taxonomyId;

            if (name.Equals("X") || name.Equals("x"))
            {
                Name = "X";
                ChromosomeType = ChromosomeType.Sex;
            }
            if (name.Equals("Y") || name.Equals("y"))
            {
                Name = "Y";
                ChromosomeType = ChromosomeType.Sex;
            }
            if (name.Equals("MT") || name.Equals("mt"))
            {
                Name = "MT";
                ChromosomeType = ChromosomeType.Mitochondrial;
            }

            int chrNumber;
            if (Int32.TryParse(name, out chrNumber))
            {
                Name = name;
                ChromosomeType = ChromosomeType.Autosome;
            }

            if (Name == String.Empty)
                throw new ArgumentException("Chromosome name cannot be parsed.");
        }

        public Chromosome(string name) : this(9606, name) { }

 //       internal Chromosome(): this(9606, "1") { }

        #endregion

        #region Conversion methods

        public static Chromosome Parse(string chrStr)
        {
            var match = Regex.Match(chrStr, @"(chr)?(?<chrName>[0-9xy]+)");
            var group = match.Groups;
            var chrName = group["chrName"].Value.ToUpper();
            if (chrName[0] == '0')
                chrName = chrName.Substring(1);

            return new Chromosome(chrName);
        }

        public static bool TryParse(string chrStr, out Chromosome result)
        {
            result = null;

            try
            {
                result = Parse(chrStr);
            }
            catch
            {
                return false;
            }

            return true;
        }

        #endregion

        #region ICloneable implementation

        /// <summary>
        /// Creates an exact copy of current Chromosome.
        /// </summary>
        /// <returns></returns>
        public Chromosome Clone()
        {
            return new Chromosome(TaxonomyId, Name);
        }

        #if !(SILVERLIGHT || WINDOWS_PHONE)

        /// <summary>
        /// Creates an exact copy of current Chromosome as an object. 
        /// </summary>
        /// <returns></returns>
        object ICloneable.Clone()
        {
            return Clone();
        }

        #endif

        #endregion

        #region INamed implementation

        /// <summary>Gets or sets chromosome name.</summary>
        [DataMember]
        public string Name { get; set; }

        #endregion

        #region IComparable, IComparable<Chromosome>, IEquatable<Chromosome> implementation

        public int CompareTo(object other)
        {
            if (other == null)
                throw new ArgumentNullException("other");

            if (!(other is Chromosome))
                throw new InvalidCastException("The 'other' argument is not a Chromosome object.");

            return CompareTo(other as Chromosome);
        }

        public int CompareTo(Chromosome other)
        {
            if (ChromosomeType.Equals(other.ChromosomeType))
            {
                switch (ChromosomeType)
                {
                    case ChromosomeType.Autosome:
                        return Int32.Parse(Name).CompareTo(Int32.Parse(other.Name));
                    case ChromosomeType.Sex:
                        return String.CompareOrdinal(Name, other.Name);
                }
            }

            return ChromosomeType.CompareTo(ChromosomeType);
        }

        public bool Equals(Chromosome other)
        {
            return TaxonomyId.Equals(other.TaxonomyId) & Name.Equals(other.Name);
        }

        #endregion

        #region Object overriden

        public override int GetHashCode()
        {
            return TaxonomyId ^ Name.GetHashCode();
        }

        public override bool Equals(object other)
        {
            if (other == null)
                throw new ArgumentNullException("other");

            if (!(other is Chromosome))
                throw new InvalidCastException("The 'other' argument is not a Chromosome object.");
            
            return Equals(other as Chromosome);
        }

        public override string ToString()
        {
            return String.Format("Chr{0}", Name);
        }

        #endregion
    }
}
